package com.stars.easyms.logger.support.logback.handler;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import org.slf4j.Marker;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;

/**
 * <p>className: EasyMsLoggingEventFactory</p>
 * <p>description: 日志事件工厂类</p>
 *
 * @author guoguifang
 * @date 2019-11-05 14:47
 * @since 1.3.3
 */
final class EasyMsLoggingEventFactory {

    private static final EasyMsLoggingEventFactory INSTANCE = new EasyMsLoggingEventFactory();

    private final Queue<EasyMsLoggingEvent> eventQueue = new LinkedBlockingQueue<>();

    private final AtomicLong passCount = new AtomicLong();

    private volatile long lastCheckTime = System.currentTimeMillis();

    private volatile long tps;

    EasyMsLoggingEvent getEasyMsLoggingEvent(Logger logger, Marker marker, Level level, String message, Throwable throwable, Object[] argArray) {
        EasyMsLoggingEvent loggingEvent = eventQueue.poll();
        if (loggingEvent == null) {
            loggingEvent = new EasyMsLoggingEvent(logger, marker, level, message, throwable, argArray);
        } else {
            loggingEvent.set(logger, marker, level, message, throwable, argArray);
        }
        passCount.getAndIncrement();
        return loggingEvent;
    }

    void offer(EasyMsLoggingEvent event) {
        if (isOffer() && event.appenderRelease() == 0) {
            eventQueue.offer(event.clear());
        }
    }

    private boolean isOffer() {
        int minCheckIntervalTime = 5000;
        long currTime = System.currentTimeMillis();
        boolean flag = false;
        if (currTime - lastCheckTime > minCheckIntervalTime) {
            synchronized (this) {
                long intervalTime = currTime - lastCheckTime;
                if (intervalTime > minCheckIntervalTime) {
                    lastCheckTime = currTime;
                    tps = passCount.getAndSet(0) * 1000 / intervalTime;
                    int maxQueueSize = 1000000;
                    tps = tps <= maxQueueSize ? tps : maxQueueSize;
                    flag = true;
                }
            }
        }
        long diffValue = eventQueue.size() - tps;
        if (diffValue < 0) {
            return true;
        }
        if (diffValue > 0 && flag) {
            long removeCount = diffValue / 2;
            for (int i = 0; i < removeCount; i++) {
                if (eventQueue.poll() == null) {
                    break;
                }
            }
        }
        return false;
    }

    static EasyMsLoggingEventFactory getInstance() {
        return INSTANCE;
    }

    private EasyMsLoggingEventFactory() {
    }

}